/*
 * CMediaStorage.h
 *
 *  Created on: 2017年3月3日
 *      Author: chuanjiang.zh
 */

#ifndef CMEDIASTORAGE_H_
#define CMEDIASTORAGE_H_

#include "MediaStorage.h"
#include "CMediaStorageChannel.h"
#include <vector>
#include "DataStore.h"
#include "CDiskSpaceMonitor.h"
#include "RecordClearThread.h"
#include "TMap.h"


namespace av
{


typedef std::vector< MediaStorageChannelPtr >	StorageChannelArray;

typedef comn::Map< MediaStorageChannel*, CMediaStorageChannelPtr>    MediaStorageChannelMap;


class CMediaStorage:
        public av::MediaStorage,
		public av::MediaStorageSink,
		public DiskSpaceMonitorListener,
		public RecordClearSink
{
public:
	/// 一次删除多少个录像
	static const size_t	DELETE_RECORD_COUNT = 10;


public:
	CMediaStorage();
	virtual ~CMediaStorage();

	virtual bool open(const char* dbpath);
	virtual void close();
	virtual bool isOpen();


    /**
     * 打开存储通道
     * @param device 设备标识
     * @return 存储通道
     */
    virtual MediaStorageChannel* openChannel(const char* device);

    /**
     * 关闭媒体存储
     */
    virtual void closeChannel(MediaStorageChannel* channel);

    /**
     * 判断媒体存储是否打开
     * @return
     */
    virtual bool isChannelOpen(const char* device);


    /**
     * 设置录像文件格式
     * 默认为TS文件
     * @param fileFormat
     */
    virtual void setFileFormat(RecordFileFormat fileFormat);

    /**
     * 设置录像文件时长
     * 默认值为 StorageConst.DEFAULT_FILE_DURATION
     * 录像达到设定时长时, 切换文件
     * 切换文件发生在关键帧处, 因此,实际的录像文件时长有偏差, 偏差大小为关键帧间隔
     * @param seconds
     */
    virtual void setFileDuration(int seconds);

    virtual int findRecord(const MRecordQuery& query, int offset, MRecord* records, int size);

    virtual int countRecord(const MRecordQuery& query);

    virtual bool getRecord(int id, av::MRecord& record);

    /**
     * 记录报警事件
     * @param device    设备标识
     * @param event 事件类型 @see AlarmEventType
     * @param name  事件名称
     * @param memo  事件描述
     * @return
     */
    virtual bool writeEvent(const char* device, int event, const char* name, const char* memo);

    /**
     * 查找报警
     * @param device 设备标识, 为空 表示忽略
     * @param fromTime  起始时间, <= 0 表示忽略
     * @param toTime    结束时间, <= 0 表示忽略
     * @param types     事件类型集合, <=0 表示忽略
     * @param events    事件数组
     * @param size      数组大小
     * @return
     */
    virtual int findEvent(const char* device, time_t fromTime, time_t toTime, int types, AlarmEvent* events, int size);

    /**
     * 设置事件回调
     * @param sink
     */
    virtual void setEventSink(MediaStorageSink* sink);

    /**
     * 设置录像目录
     * @param path  目录
     * @param percentage    磁盘最大使用比例. 取值范围: (0.0, 1.0)
     * @return
     */
    virtual bool setRecordDir(const char* path, float percentage);

    /**
     * 删除录像
     * @param id    record 标识
     * @return
     */
    virtual bool deleteRecord(int id);


    /**
     * 总是启用AAC编码.
     * 当音频流是G.711时, 就转码为AAC
     * 在 startRecord 之前调用.
     * @param enabled   true 表示启用AAC编码
     */
    virtual void forceAac(bool enabled);

    /**
     * 设置循环覆盖录像数量
     * 磁盘空间不够时, 自动删除指定数量的录像,默认值为 10
     * @param count <= 0 表示不循环覆盖. 即空间不足时,自动停止录像
    */
    virtual void setCoverageRecord(int count);

    /**
     * 通过文件名删除录像
     * @param filename
     * @return true on success
    */
    virtual bool deleteRecordWithFileName(const char* filename);

    /**
    * 是否在录像目录下为各通道创建子目录, 目录名称为通道编号.
    * 初始化后, 在 open 之前调用, 此后不要更改.
    * @param enabled    true 表示启用子目录
    */
    virtual void enableRecordSubDir(bool enabled);

    /**
    * 设置录像文件名称的模板.
    * 使用 strftime 的格式化符号表示. {c} 表示通道, {d} 表示从1开始算起的通道号, {n} 表示通道名称.
    * 文件名后缀是自动加上的. 模板中无须添加.
    * 模板中不应该出现文件名禁止的字符, 比如目录分隔符.
    * 比如: %Y%m%d_%H%M%S_{c}, 格式化后的名称是: 20170731_10:30:50_0
    * 默认的模板为: {c}_%Y%m%d_%H%M%S
    * @param tmpl   录像文件名称模板
    */
    virtual void setRecordNameTemplate(const char* tmpl);

    /**
     * 根据文件名查找录像
     * return true 表示找到
    */
    virtual bool findRecordWithFileName(const char* filename, MRecord* record);

    /**
    * 允许覆盖事件录像
    * return events 表示不覆盖事件录像， 其他值表示事件组合. 0xFFFFFFFF 表示所有事件
    */
    virtual void enableCoverageEventRecord(int events);

    /**
     * 清理损坏的记录.
     * 记录的文件不存在时, 被认为是损坏的.
     * 内部启用独立线程, 遍历删除. 不会阻塞该调用.
     * @param device 为空 表示清理所有的记录
     */
    virtual void clearBrokenRecord(const char* device);


	virtual void onNotEnoughSpace(const std::string& dirpath, int64_t totalSpace, int64_t freeSpace, double percent);

	virtual void onRecordCleared(const char* device, int cleared);

	virtual void onStorageEvent(const StorageEvent& event);


public:
	void setDbPath(const char* dbPath);

protected:

	void fireStorageEvent(const StorageEvent& event);

	void handleNotEnoughSpace(const std::string& dirpath, int64_t totalSpace, int64_t freeSpace);

	void deleteRecord(av::MRecord& record);

	bool writeEvent(const char* device, AlarmEvent& alarmEvent);

	void doSetRecordDir(const std::string& filepath);
	void doSetFileFormat(RecordFileFormat fileFormat);
	void doSetFileDuration(int seconds);
	void doStopRecord();

	CMediaStorageChannelPtr findChannel(const char* device);

protected:
	MediaStorageChannelMap	m_channels;
	RecordFileFormat	m_fileFormat;
	int	m_fileDuration;
	std::string	m_dirPath;

	MediaStorageSink*	m_sink;
	DataStorePtr	m_dataStore;
	CDiskSpaceMonitor	m_diskMonitor;

	int		m_coverageRecord;
	int		m_coverageEvents;

	std::string	m_dbPath;

	std::shared_ptr< comn::Thread >	m_clearThread;

};


}
#endif /* CMEDIASTORAGE_H_ */
